/**
 * Copyright (c) 2020 kedacom
 * OpenATC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 * http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 **/
package com.openatc.agent.controller;

import algorithm.Kdalgorithm;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.openatc.agent.model.Interfeature;
import com.openatc.agent.model.Route;
import com.openatc.agent.model.RouteIntersection;
import com.openatc.agent.model.RouteIntersectionBase;
import com.openatc.agent.resmodel.DevList;
import com.openatc.agent.resmodel.RealtimeOffset;
import com.openatc.agent.service.MessageService;
import com.openatc.agent.service.RouteDao;
import com.openatc.agent.service.RouteService;
import com.openatc.agent.service.arterial.ArterialTrafficControlExecutor;
import com.openatc.agent.service.arterial.exception.IntersectionCycleException;
import com.openatc.agent.service.arterial.exception.KeyAgentIDException;
import com.openatc.agent.service.arterial.exception.UnsupportedArterialTrafficControlException;
import com.openatc.agent.service.impl.CommonService;
import com.openatc.agent.service.impl.WebOperationCollector;
import com.openatc.agent.service.impl.WebOperationRecordExtractor;
import com.openatc.comm.common.CosntDataDefine;
import com.openatc.comm.data.MessageData;
import com.openatc.core.common.Constants;
import com.openatc.core.common.IErrorEnumImplInner;
import com.openatc.core.common.IErrorEnumImplOuter;
import com.openatc.core.model.InnerError;
import com.openatc.core.model.RESTRet;
import com.openatc.core.model.RESTRetBase;
import com.openatc.core.util.GsonUtil;
import com.openatc.core.util.RESTRetUtils;
import com.openatc.extend.common.aop.model.SysLog;
import com.openatc.model.model.Feature;
import com.openatc.model.model.StatusPattern;
import model.RouteIntsection;
import model.RouteOpt;
import model.RoutePara;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import java.net.SocketException;
import java.text.ParseException;
import java.util.*;
import java.util.logging.Logger;

import static com.openatc.core.common.IErrorEnumImplOuter.*;

/**
 * @author kedacom
 * @ClassName: RouteController
 * @Description:
 * @date 2019年12月4日 下午16:34:17
 */
@RestController
public class RouteController {
    private static final Logger logger = Logger.getLogger(RouteController.class.toString());
    @Autowired(required = false)
    private RouteDao routeDao;
    @Autowired
    private ArterialTrafficControlExecutor executor;
    @Autowired
    private MessageService messageService;

    @Autowired
    private RouteService routeService;

    @Autowired
    private CommonService commonService;

    Sort.Order order = Sort.Order.asc("id");
    Sort sort = Sort.by(order);

    /**
     * @return RESTRetBase
     * @Title: getRoutes
     * @Description: 查询所有协调路线的全部信息
     */
    @GetMapping(value = "/route")
    public RESTRetBase getRoute() {
        //获取全部路线
        List<Route> routes = routeService.getRouteList(sort);
        return RESTRetUtils.successObj(routes);
    }

    /**
     * @return RESTRetBase
     * @Title: getRouteCondition
     * @Description: 根据条件查询协调路线
     */
    @GetMapping(value = "/route/search/{condition}")
    public RESTRetBase getRouteCondition(@PathVariable String condition) {
        //获取全部路线
        List<Route> routes = routeService.getRouteListSearch(condition,sort);
        return RESTRetUtils.successObj(routes);
    }


    /**
     * @return RESTRetBase
     * @Title: getRoutes
     * @Description: 查询单个协调路线的全部信息
     */
//    @RequiresRoles(value = {"admin","superadmin"},logical = Logical.OR)
    @GetMapping(value = "/route/{id}")
    public RESTRetBase getRouteById(@PathVariable Long id) {
//        logger.info("getRouteById");
        return RESTRetUtils.successObj(routeDao.findById(id));
    }

    /**
     * @return RESTRetBase
     * @Title: getRoutes
     * @Description: 查询全部协调路线的简略信息
     */
//    @RequiresRoles(value = {"admin","superadmin"},logical = Logical.OR)
    @GetMapping(value = "/route/simple")
    public RESTRetBase getRouteByName() {
        return RESTRetUtils.successObj(routeDao.getSimpleInfoForRoute());
    }

    /**
     * @return RESTRetBase
     * @Title: addRoutes
     * @Description: 新增协调路线
     */
    @SysLog(collector = WebOperationCollector.class,extractor = WebOperationRecordExtractor.class)
    @RequiresPermissions("openatc:base:route")
    @PostMapping(value = "/route")
    public RESTRetBase addRoutes(@RequestBody Route routeEntity) {
        Route r = routeDao.findByName(routeEntity.getName());
        //校验路线名称是否重复
        if (r != null) {
            return RESTRetUtils.errorObj(E_5001);
        }
        Route dbRoute = routeDao.save(routeEntity);
        return RESTRetUtils.successObj(dbRoute);
    }

    /**
     * @return RESTRetBase
     * @Title: updateRoute
     * @Description: 更新协调路线
     */
    @SysLog(collector = WebOperationCollector.class,extractor = WebOperationRecordExtractor.class)
    @RequiresPermissions("openatc:base:route")
    @PutMapping(value = "/route")
    public RESTRetBase updateRoute(@RequestBody Route routeEntity) {
        Long id = routeEntity.getId();
//        routeIntersectionDao.deleteByRouteid(id);
        Route byId = routeDao.findById(id);
        routeEntity.setAreaid(byId.getAreaid());
        // 查验是否修改路线名称
        if (!byId.getName().equals(routeEntity.getName())) {
            Route r = routeDao.findByName(routeEntity.getName());
            if (r != null) {
                return RESTRetUtils.errorObj(E_5001);
            }
            routeEntity.setOptPatternList(byId.getOptPatternList());
            Route dbRoute = routeDao.save(routeEntity);
            return RESTRetUtils.successObj(dbRoute);
        } else {
//            // 限制存在方案的路线修改
//            if (routeEntity.getOptPatternList() != null) {
//                return RESTRetUtils.errorObj(E_5004);
//            }
//            routeEntity.setOptPatternList(null);
            // 根据路口变化更新原有的协调方案
            if (routeEntity.getOptPatternList() != null) {
                routeService.initPatternList(routeEntity);
            }

            Route dbRoute = routeDao.save(routeEntity);
            return RESTRetUtils.successObj(dbRoute);
        }

    }

//    /**
//     * @return RESTRetBase
//     * @Title: updateRoute
//     * @Description: 更新协调路线名称
//     */
//    @Deprecated
//    @RequiresPermissions("sys:base:route")
//    @PutMapping(value = "/route/name")
//    public RESTRetBase updateRouteName(@RequestBody Route routeEntity) {
//        Long id = routeEntity.getId();
//        Route route = routeDao.findById(id);
//        route.setName(routeEntity.getName());
//        try {
//            routeDao.save(route);
//        } catch (Exception e) {
//            return RESTRetUtils.errorObj(E_5001);
//        }
//        return RESTRetUtils.successObj(route);
//    }

    /**
     * @return RESTRetBase
     * @Title: deleteRoute
     * @Description: 删除单个协调路线
     */
    @SysLog(collector = WebOperationCollector.class,extractor = WebOperationRecordExtractor.class)
    @RequiresPermissions("openatc:base:route")
    @DeleteMapping(value = "/route/{id}")
    public RESTRetBase deleteRoute(@PathVariable Long id) {
        routeDao.deleteById(id);
        logger.info("deleteRoute");
        return RESTRetUtils.successObj();
    }

    /**
     * @return RESTRetBase
     * @Title: generate
     * @Description: 算法生成
     */
//    @RequiresRoles(value = {"admin","superadmin"},logical = Logical.OR)
    @PostMapping(value = "/route/generate")
    public RESTRetBase generate(@RequestBody RoutePara routePara) {
        List<RouteIntsection> devs = routePara.getDevs();
        // 没有协调路口
        if (CollectionUtils.isEmpty(devs)){
            return RESTRetUtils.errorObj(IErrorEnumImplOuter.E_5003);
        }
        try {
            RouteOpt routeOpt = executor.execute(routePara);
            return RESTRetUtils.successObj(routeOpt);
        } catch (UnsupportedArterialTrafficControlException e) {
            return RESTRetUtils.errorObj(E_5005);
        } catch (IntersectionCycleException e) {
            return RESTRetUtils.errorObj(E_5002);
        } catch (KeyAgentIDException e) {
            return RESTRetUtils.errorObj(E_5006);
        }
    }

    /**
     * @return RESTRetBase
     * @Title: bandwidth
     * @Description: 计算路线协调带宽
     */
    @PostMapping(value = "/route/bandwidth")
    public RESTRetBase greenwavewidth(@RequestBody RoutePara routePara) {
        double intsvelup = 0.00;
        double intsveldown = 0.00;
        intsvelup = routePara.getUpspeed();
        intsveldown = routePara.getDownspeed();
        List<RouteIntsection> deviceList = routePara.getDevs();
        String type  = routePara.getOptstrategy();
        String direction = routePara.getDirection();
        int intsoffset[] = new int[deviceList.size()]; // 保存优化后的相位差
        String keyAgentid = routePara.getKeyintsid();

        for (RouteIntsection dev : deviceList) {
            // 保存各路口相位差
            intsoffset[dev.getSortid() - 1] = dev.getFeature().getPatternList().get(0).getOffset();
        }

        Kdalgorithm kdalgorithm = new Kdalgorithm();
        RouteOpt routeOpt = new RouteOpt();
        routeOpt.setGreenwave( kdalgorithm.getBandWidth(type,direction,intsvelup, intsveldown, deviceList,intsoffset,keyAgentid ) );

        return RESTRetUtils.successObj(routeOpt);

        // 红波协调
//        if ("red".equals(routePara.getOptstrategy())){
//            return routeOptStrategy.getRedRouteOpt(routePara);
//        }
//        // 队尾协调
//        else if ("motorcade-end".equals(routePara.getOptstrategy())){
//            return routeOptStrategy.getLastOfMotorcadeRouteOpt(routePara);
//        }
        // todo 动态绿波，按照绿波优化实现，算法完成后再替换
//        else if ("dynamicGreen".equals(routePara.getOptstrategy())) {
//            return routeOptStrategy.getGreenRouteOpt(routePara);
//        }
//        // 默认绿波协调
//        else{
//            return routeOptStrategy.getGreenRouteOpt(routePara);
//        }
//        return routeOpt == null ? RESTRetUtils.errorDetialObj(IErrorEnumImplOuter.E_5002,new InnerError()) : RESTRetUtils.successObj(routeOpt);

    }
    
    /**
     * @return RESTRetBase
     * @Title: uploadConfigure
     * @Description: 计算实时相位差
     */
    @GetMapping(value = "/route/realtimeoffset/{routeid}/{keyagentid}/{keyoffset}")
    public RESTRetBase realtimeoffset(@PathVariable Long routeid,@PathVariable String keyagentid, @PathVariable int keyoffset) {

        List<RealtimeOffset> devList = new ArrayList<>();
        Route rr = routeDao.findById(routeid);
        Set<RouteIntersection> routeIntersections = rr.getDevs();
        int keyCurtime = 0;
        int keyCycle = 0;

//        if( rr.getOptPatternList() == null ){
//            return RESTRetUtils.errorObj(E_5008);
//        }



        // 获取实时方案状态
        for (RouteIntersection r : routeIntersections) {

            MessageData messageData = new MessageData(r.getAgentid(), CosntDataDefine.getrequest, "status/pattern", new JsonObject());
            RESTRet<MessageData> retBase = messageService.postDevsMessage(messageData);

            if(retBase.getData() == null){
                InnerError innerError = RESTRetUtils.innerErrorObj(r.getAgentid(), IErrorEnumImplInner.E_201, null);
                return RESTRetUtils.errorDetialObj(E_4001,innerError);
            }
            if(retBase.getData().getData() == null){
                InnerError innerError = RESTRetUtils.innerErrorObj(r.getAgentid(), IErrorEnumImplInner.E_301, null);
                return RESTRetUtils.errorDetialObj(E_4001,innerError);
            }



            RealtimeOffset ro = new RealtimeOffset();

            StatusPattern statusPattern = GsonUtil.getInstance().fromJson(retBase.getData().getData().getAsJsonObject(), StatusPattern.class);

            retBase.getData().getData().getAsJsonObject();
            ro.setAgentid( r.getAgentid() );
            ro.setCurtime( statusPattern.getCurTime() );
            ro.setCycle( statusPattern.getCycle() );

            devList.add(ro);

            if( keyagentid.equals( r.getAgentid() ) ){
                keyCycle = statusPattern.getCycle();
                keyCurtime = statusPattern.getCurTime();
            }
        }

        // 计算实时相位差
        for( RealtimeOffset offset : devList ){
            int realoffset = offset.getCurtime() - keyCurtime + keyoffset;
            offset.setOffset( realoffset );
        }

        return RESTRetUtils.successObj(devList);
    }


    /**
     * @return RESTRetBase
     * @Title: uploadConfigure
     * @Description: 路线整体上传方案
     */
//    @RequiresRoles(value = {"admin","superadmin"},logical = Logical.OR)
    @GetMapping(value = "/route/{feature}/upload/{id}")
    public RESTRetBase uploadConfigure(@PathVariable String feature, @PathVariable Long id) throws SocketException, ParseException {
//      定义该接口的返回对象
        DevList devList = new DevList();
        // 查询当前路线中是否存在离线路口
//        List<String> offLineDev = ascsDao.findOfflineDevId(id);
//        if (!CollectionUtils.isEmpty(offLineDev)) {
//            InnerError innerError = RESTRetUtils.innerErrorObj(offLineDev.get(0), IErrorEnumImplInner.E_301, null);
//            return RESTRetUtils.errorDetialObj(E_4001,innerError);
//        }
        //定义该对象的值为list列表
        List<RouteIntersectionBase> devs = new ArrayList<>();
        //拿到各个列表的id，再根据id进行请求获得数据后，存放到list当中
        Route rr = routeDao.findById(id);
        Set<RouteIntersection> routeIntersections = rr.getDevs();

        //为每一个设备设置id和feature
        for (RouteIntersection r : routeIntersections) {
            //创建返回的设备
            RouteIntersectionBase eachDev = new RouteIntersectionBase();
            //为设备设置id
            eachDev.setAgentid(r.getAgentid());
            //为设备设置feature
            MessageData messageData = new MessageData(r.getAgentid(), CosntDataDefine.getrequest, "feature/" + feature, new JsonObject());
            RESTRet<MessageData> retBase = null;
//            retBase = messageController.postDevsMessage(null, messageData);
            retBase = messageService.postDevsMessage(messageData);

            if (!retBase.getCode().equals(Constants.SUCCESS_CODE)) {
                return retBase;
            }
            if(retBase.getData() == null){
                InnerError innerError = RESTRetUtils.innerErrorObj(r.getAgentid(), IErrorEnumImplInner.E_201, null);
                return RESTRetUtils.errorDetialObj(E_4001,innerError);
            }
            if(retBase.getData().getData() == null){
                InnerError innerError = RESTRetUtils.innerErrorObj(r.getAgentid(), IErrorEnumImplInner.E_201, null);
                return RESTRetUtils.errorDetialObj(E_4001,innerError);
            }

            Interfeature intersectionFeature = new Interfeature();
            if (feature.equals("pattern")) {
                intersectionFeature.setPatternList(retBase.getData().getData().getAsJsonObject().get("patternList"));
            }
            if (feature.equals("phase")) {
                intersectionFeature.setPhaseList(retBase.getData().getData().getAsJsonObject().get("phaseList"));
            }
            eachDev.setFeature(intersectionFeature);
            devs.add(eachDev);
        }
        devList.setDevs(devs);
        return RESTRetUtils.successObj(devList);
    }


    /**
     * @return RESTRetBase
     * @Title: downloadConfigure
     * @Description: 路线整体下载方案
     */
    @SysLog(collector = WebOperationCollector.class,extractor = WebOperationRecordExtractor.class)
//    @RequiresRoles(value = {"admin","superadmin"},logical = Logical.OR)
    @PostMapping(value = "/route/{feature}/download")
    public RESTRetBase downloadConfigure(@PathVariable String feature, @RequestBody JsonObject jsonObject) throws SocketException, ParseException {

        //先拿到pattern的jsonObject对象feature
        JsonArray devlistJsonArray = jsonObject.get("devs").getAsJsonArray();
        String intersectionid;
        JsonObject featureList;
        for (JsonElement device : devlistJsonArray) {
            intersectionid = device.getAsJsonObject().get("agentid").getAsString();
            //featureList就是要发送的patternList
            featureList = device.getAsJsonObject().get("feature").getAsJsonObject();
            MessageData messageData = new MessageData(intersectionid, CosntDataDefine.setrequest, "feature/" + feature, featureList);

            RESTRet<MessageData> retBase = messageService.postDevsMessage(messageData);
            if (retBase.getMessage().equals("Device not online!")) {
                InnerError devCommError = RESTRetUtils.innerErrorObj(intersectionid, IErrorEnumImplInner.E_301, retBase.getData());
                return RESTRetUtils.errorDetialObj(E_4003, devCommError);
            }
        }
        return RESTRetUtils.successObj();
    }


    /**
     * 更新路线中的优化方案
     * @param routeEntity
     * @return
     */
    @SysLog(collector = WebOperationCollector.class,extractor = WebOperationRecordExtractor.class)
    @RequiresPermissions("openatc:base:route")
    @PutMapping("/route/pattern")
    public RESTRetBase updateRoutePattern(@RequestBody Route routeEntity) {
        Long id = routeEntity.getId();
        int compatibility = routeEntity.getCompatibility();
        Route dbRoute = routeDao.findById(id);
        dbRoute.setCompatibility(compatibility);
        dbRoute.setOptPatternList(routeEntity.getOptPatternList());
        return RESTRetUtils.successObj(routeDao.save(dbRoute));
    }

    /**
     * 向信号机下发优化后的方案
     * @return
     */
    @SysLog(collector = WebOperationCollector.class,extractor = WebOperationRecordExtractor.class)
    @PostMapping("/route/opt/{feature}/download")
    public RESTRetBase downOptPattern(@RequestBody Route routeEntity,@PathVariable(value = "feature") String feature) {
        if (!"pattern".equals(feature)) {
            return RESTRetUtils.errorObj(E_1001);
        }
        boolean continueOnfailure = routeEntity.getContinueOnfailure();
        JsonArray optPatternList = routeEntity.getOptPatternList();
        JsonElement optPattern = optPatternList.get(0);
        JsonArray devJsonArray = optPattern.getAsJsonObject().get("devs").getAsJsonArray();

        // 获取当前协调方案中的路口id列表
        List<String> agentids = routeService.getAgentIds(devJsonArray);
        List<MessageData> messageDatas = new ArrayList<>();
        // 应答的方案执行情况
        JsonObject statusdata = new JsonObject();
        JsonArray data = new JsonArray();
        statusdata.add("statusdata",data);

        Map<String,JsonElement> agentRet = new HashMap<>();

        for (String agentid : agentids) {
            MessageData messageData = new MessageData(agentid,"get-request","feature/all");
            RESTRet ret = messageService.postDevsMessage( messageData);
            boolean successSendMsg = commonService.isSuccessSendMsg(ret);
            // 这里遇到失败，下面的下载也肯定会失败，所以直接把这个失败的消息 放入应答消息中
            if (!successSendMsg) {
                // 把设备的状态添加到map中
                addAgentRet(ret,agentRet,messageData.getAgentid());
                if (continueOnfailure) {
                    continue;
                } else {
                    break;
                }
            }

            messageDatas.add((MessageData) (ret.getData()));
        }
        // 获取信号机当前协调方案中配置路口的参数
        Map<String,Feature> featureMap = routeService.getCrossFeature(messageDatas);
        // 构建下发参数结构
        List<MessageData> messageDataList;
        int compatibility = routeEntity.getCompatibility();
        // OpenATC信号机，新增方案，计划，日期
        if (compatibility == 0) {
            messageDataList = routeService.buildFeatureStructure(featureMap,optPatternList,routeEntity.getName());
        } else {
            // 兼容第三方信号机,只修改方案参数
            messageDataList = routeService.buildCompatibleFeature(featureMap, optPatternList);
        }

        // 下载日期、计划、方案参数
        for (MessageData messageData : messageDataList) {
            RESTRet ret = messageService.postDevsMessage( messageData);

            boolean successSendMsg = commonService.isSuccessSendMsg(ret);
            addAgentRet(ret,agentRet,messageData.getAgentid());
            // 如果遇到失败，且指定下发失败就不再继续下发，直接应答当前状态
            if (!continueOnfailure && !successSendMsg) {
                for (String agentid : agentRet.keySet()) {
                    data.add(agentRet.get(agentid));
                }
                return RESTRetUtils.successObj(statusdata);
            }
        }

        for (String agentid : agentRet.keySet()) {
            data.add(agentRet.get(agentid));
        }

//      程序运行到此处，说明方案都下发成功了，保存方案
        Long id = routeEntity.getId();
        Route dbRoute = routeDao.findById(id);
        Route retRoute = routeService.updatePatternList(routeEntity, dbRoute);
        routeDao.save(retRoute);
        return RESTRetUtils.successObj(statusdata);
    }

    private void addAgentRet(RESTRet ret, Map<String, JsonElement> agentRet,String agentid) {
        JsonElement currentDevStatus = agentRet.get(agentid);
        // 为空 直接加进去
        if (currentDevStatus == null) {
            agentRet.put(agentid, commonService.addPropertyForRet(ret,agentid));
            return;
        }
        if (commonService.isSuccessMsgElement(currentDevStatus)) {
            agentRet.put(agentid, commonService.addPropertyForRet(ret,agentid));
        }
    }

    /**
     * 删除线路中指定的方案
     * @return
     */
    @DeleteMapping("/route/opt/pattern")
    public RESTRetBase deleteOptPattern(@RequestBody JsonObject jsonObject) {
        JsonElement routeidEle = jsonObject.get("routeid");
        JsonElement optpatternidEle = jsonObject.get("optpatternid");
        if (routeidEle == null || optpatternidEle == null) {
            return RESTRetUtils.errorObj(E_1001);
        }
        Long routeid = routeidEle.getAsLong();
        int patternid = optpatternidEle.getAsInt();
        // 根据路线id获取路线
        Route dbroute = routeDao.findById(routeid);
        // 移除指定方案
        JsonArray optPatternList = dbroute.getOptPatternList();
        JsonElement needRemovePattern = routeService.getNeedRemovePattern(patternid,optPatternList);
        // 保存
        optPatternList.remove(needRemovePattern);
        routeDao.save(dbroute);
        return RESTRetUtils.successObj(true);
    }
    /**
     * 重置线路方案
     * @return
     */
    @PostMapping("/route/pattern/reset")
    public RESTRetBase resetOptPattern(@RequestBody JsonObject requestParam) {
        if (!requestParam.has("routeid")) {
            return RESTRetUtils.errorObj(E_1001);
        }
        int routeId = requestParam.get("routeid").getAsInt();
        routeDao.resetOptPattern(routeId);
        JsonObject success = new JsonObject();
        success.addProperty("success","success");
        return RESTRetUtils.successObj(success);
    }

}




